package components;


import java.awt.Color;
import java.util.Hashtable;
import java.util.NoSuchElementException;
import java.util.Vector;
import java.util.AbstractMap.SimpleEntry;

import main.ProgramOptions;

public class Operator {
	
	@Override
	public String toString() {
		return name + " ";
	}

	//name of the operator
	private final String name;
	//prepost conditions
	private final Vector<PrePostCond> prepost;
	//prevail conditions
	private final Vector<PrevailCond> prevail;
	//cost to use operator
	private final double cost;
	//agent operator is used (default -1 when nothing happened yet 0 if public)
	private int agent;
	//is public?
	private boolean isPublic;
	
	private boolean isSource;
	private boolean isSink;
	
	/**
	 * CONSTRUCTOR
	 * @param n name
	 * @param prep prepost conditions
	 * @param prev prevail conditions
	 * @param c cost
	 */
	public Operator(String n,Vector<PrePostCond> prep,Vector<PrevailCond> prev,double c){
		name=n;
		prepost=prep;
		prevail=prev;
		cost=c;
		agent=-1;
		isPublic=false;
		this.isSource = false;
		this.isSink = false;
	}
	
	/**
	 * CONSTRUCTOR
	 * @param string an operator with no state ... only neme
	 */
	public Operator(String string) {
		name=string;
		prepost=new Vector<PrePostCond>();
		prevail=new Vector<PrevailCond>();
		cost=1;
		agent=-1;
		isPublic=false;
	}
	
	
	/**
	 * making the operator and all his variables public
	 */
	public void makePublic(){
		isPublic=true;
		
	}

	/**
	 * checks if the operator should have an edge from the source vertex
	 * @return
	 */
	public boolean confirm(){
		boolean ans=true;
		for (PrePostCond i:prepost)
			ans &= i.confirm();
		for (PrevailCond i:prevail)
			ans &= i.confirm();
		return ans;
	}
	
	
	/**
	 * output string
	 * @return
	 * @throws Exception if not ready to write because no agent yet
	 */
	public String toString2() throws Exception{
		if (agent==-1) throw new Exception ("operator not ready to write yet");
		String x=name+"\n"+prevail.size()+"\n";
		for(PrevailCond i:prevail){
			x=x+i.toString2();
		}
		x=x+prepost.size()+"\n";
		for(PrePostCond i:prepost){
			x=x+i.toString2();
		}
		
		return x+(int)cost+"\n"+agent+"\n"+(isPublic?1:0)+"\n";
		
	}
/**
 * GETTER
 * @return
 */
	public double getWeight() {
		return cost;
	}


	/**
	 * function used to determine which color the operator will be painted in the GUI
	 * @return color of the oprtator 
	 */
	public Color getPaintingColor(){
		if (this.isSink|| this.isSource)
			return Color.YELLOW;
		return Color.GREEN;
		
	}
	
	
	/**
	 * checks if should be a directed edge between operator a and b
	 * @param a
	 * @param b
	 * @return true or false :)
	 * 
	 */
	public static boolean checkRelation(Operator a, Operator b) {
		Vector<Variable> aprepost = a.getPrePostVar();
		Vector<Variable> bprepost = b.getPrePostVar(true);
		Vector<Variable> bprevail = b.getPrevailVar();
		
		if (ProgramOptions.optionEdgeCount == ProgramOptions.EdgeCount.MORE){
			for (Variable i: aprepost)
				if (bprepost.contains(i) || bprevail.contains(i))
					return true;
			return false;
		}
		
		else {	//LESS
			for(Variable v : aprepost){
				if (bprepost.contains(v) && 
						((a.getVarEffect(v) == b.getVarValue(v)) || (a.getVarValue(v) == b.getVarValue(v))))
					return true;
				if (bprevail.contains(v) &&
						((a.getVarEffect(v) == b.getPrevailVal(v)) || (a.getVarValue(v) == b.getPrevailVal(v))))
					return true;
			}
			
			return false;
		}
	}
	
	//returns the value of variable v as declared in prevail vector
	private int getPrevailVal(Variable v) throws NoSuchElementException {
		if (! this.getPrevailVar().contains(v))
			throw new NoSuchElementException();
		else{
			for(PrevailCond prevail : this.getPrevailVector()){
				if (prevail.getVar() == v)
					return prevail.getValue();
			}
		}
		
		return -100;	//error
	}
	
	public Vector<PrevailCond> getPrevailVector(){
		return this.prevail;
	}
	
	//returns pre-value of variable v of the operator (in preCond vector)
	private int getVarValue(Variable v) throws NoSuchElementException {
		if (! this.getPrePostVar().contains(v))
			throw new NoSuchElementException();
		else{
			for(PrePostCond prepost : this.getPrePostVector()){
				if (prepost.getVar() == v)
					return prepost.getValue();
			}
			
			return -100;	//an error
		}
	}
	
	//returns post-effect value of variable v of the operator (in preCond vector)
	private int getVarEffect(Variable v) throws NoSuchElementException {
		if (! this.getPrePostVar().contains(v))
			throw new NoSuchElementException();
		else{
			for(PrePostCond prepost : this.getPrePostVector()){
				if (prepost.getVar() == v)
					return prepost.getEffect();
			}
			
			return -100;	//an error
		}
	}
	
	public Vector<PrePostCond> getPrePostVector(){
		return this.prepost;
	}
	
	
	/**
	 * same as get prepost var just without the variables with -1 as precond
	 * @param b
	 * @return
	 */
	private Vector<Variable> getPrePostVar(boolean b) {
		Vector<Variable> ans=new Vector<Variable>();
		for (PrePostCond i:prepost)
			if (i.getValue()!=-1)
				ans.add(i.getVar());
		return ans;
	}

/**
 * 
 * @return all variables from the prevail condintions
 */
	private Vector<Variable> getPrevailVar() {
		Vector<Variable> ans=new Vector<Variable>();
		for (PrevailCond i:prevail)
			ans.add(i.getVar());
		return ans;
	}
/**
 * 
 * @return all variables from the prepost conditions
 */
	private Vector<Variable> getPrePostVar() {
		Vector<Variable> ans=new Vector<Variable>();
		for (PrePostCond i:prepost)
			ans.add(i.getVar());
		return ans;
	}
/**
 * checks if the operator should have an edge to the sink vertex
 * @param goals
 * @return
 */
	public boolean confirm(Vector<Goal> goals) {
		boolean accept=false;
		for (Goal i:goals){
			int x=helpingGoal(i);
			if (x==-1)
				return false;
			if (x==1)
				accept=true;
		}
		return accept;
		
	}
/**
 * checks if the operator helps goal g
 * @param g the goal
 * @return
 */
	private int helpingGoal(Goal g) {
		int answer=0;
		for (PrePostCond i:prepost){
			if (i.getVar()==g.getVar())
				if (i.getValue()==g.getValue())
					return -1;
				else if (i.getEffect()==g.getValue())
					answer=1;
		}
		return answer;
	}

/**
 * sets an agent number to the operator
 * @param agentId
 */
	public void setAgent(int agentId) {
		agent=agentId;
		for (PrePostCond i:prepost)
			i.getVar().setAgent(agentId);
		for (PrevailCond i:prevail)
			i.getVar().setAgent(agentId);
			
		
	}
/**
 * setting a source operator
 * @param isSource
 */
public void setSource(boolean isSource) {
	this.isSource = isSource;
}
/**
 * GETTER
 * @return
 */
public boolean isSource() {
	return isSource;
}
/**
 * setting sink operator
 * @param isSink
 */
public void setSink(boolean isSink) {
	this.isSink = isSink;
}
/**
 * GETTER
 * @return
 */
public boolean isSink() {
	return isSink;
}

/**
 * GETTER
 * @return
 */
public int getAgentId(){
	return this.agent;
}

		
	



	
}